package com.link.base.wechat.payment.service;

import com.link.base.wechat.payment.model.CoPayToWalletPayment;
import com.link.base.wechat.payment.model.WxPayment;
import com.link.base.wechat.payment.util.PayUtils;
import com.link.base.wechat.wxlog.model.WxLogModule;
import com.link.base.wechat.wxlog.service.WxLogService;
import com.link.core.basic.service.ServiceException;
import com.link.core.util.JsonUtil;
import com.link.core.util.StringUtils;
import com.thoughtworks.xstream.XStream;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.net.InetAddress;
import java.util.*;

/**
 * 支付服务类
 *
 * @author LiQun
 * @date 2019/2/25 17:12
 */
@Service
public class WxPaymentServiceImpl implements WxPaymentService {
    @Resource
    private WxLogService wxLogService;

    /**
     * 设置支付信息
     *
     * @author LiQun
     * @date 2019/2/25 18:56
     * @param appId     小程序/微信公众号appid
     * @param mchId     商户号id
     * @param tradeNo   商户订单号
     * @param notifyUrl 通知地址 异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。
     * @param totalFee  标价金额(订单总金额)
     * @param tradeType 交易类型
     *                  JSAPI - JSAPI支付 小程序/微信公众号全部都为JSAPI
     *                  NATIVE - Native支付
     *                  APP - APP支付
     * @param attach    附加数据 在查询API和支付通知中原样返回，可作为自定义参数使用
     * @param body      商品描述
     * @param apiKey    为商户平台设置的密钥key
     * @return 支付对象
     * @throws Exception 异常信息
     */
    @Override
    public WxPayment getPayment(
            String appId,
            String mchId,
            String tradeNo,
            String notifyUrl,
            Integer totalFee,
            String tradeType,
            String body,
            String attach,
            String apiKey,
            String openId
    ) throws Exception {
        // 校验必输字段
        if (StringUtils.isBlank(tradeNo)) {
            throw new ServiceException("PAYMENT-001");
        }
        if (StringUtils.isBlank(notifyUrl)) {
            throw new ServiceException("PAYMENT-002");
        }
        if (totalFee == null || totalFee == 0) {
            throw new ServiceException("PAYMENT-003");
        }
        if (StringUtils.isBlank(tradeType)) {
            throw new ServiceException("PAYMENT-004");
        }
        if (StringUtils.isBlank(apiKey)) {
            throw new ServiceException("PAYMENT-005");
        }
        if (StringUtils.isBlank(openId)) {
            throw new ServiceException("PAYMENT-006");
        }
        WxPayment wxPayment = new WxPayment();
        // 随机数
        wxPayment.setNonceStr(PayUtils.createNonceStr());
        // 商户订单号
        wxPayment.setOutTradeNo(tradeNo);
        // appid
        wxPayment.setAppId(appId);
        // 商户号
        wxPayment.setMchId(mchId);
        // 终端ip
        InetAddress addr = InetAddress.getLocalHost();
        wxPayment.setSpbillCreateIp(addr.getHostAddress());
        // 签名类型
        wxPayment.setSignType("MD5");
        // 币种 人民币
        wxPayment.setFeeType("CNY");
        // 通知地址
        wxPayment.setNotifyUrl(notifyUrl);
        // 交易类型 JSAPI 公众号支付
        wxPayment.setTradeType(tradeType);
        // 总金额
        wxPayment.setTotalFee(totalFee);
        // 商品描述
        if (StringUtils.isBlank(body)) {
            body = "商品";
        }
        wxPayment.setBody(body);
        // openid
        wxPayment.setOpenid(wxPayment.getOpenid());
        // 附加数据 在查询API和支付通知中原样返回，可作为自定义参数使用
        wxPayment.setAttach(attach);
        // 用户openid
        wxPayment.setOpenid(openId);
        // 签名
        wxPayment.setSign(createWxPaymentSign(wxPayment, apiKey));
        wxLogService.log(WxLogModule.wxLogPay, "创建微信支付对象", JsonUtil.toString(wxPayment), appId, null);
        return wxPayment;
    }

    /**
     * 创建微信支付签名
     *
     * @author LiQun
     * @date 2019/2/26 19:01
     * @param wxPayment 微信支付对象
     * @param apiKey 为商户平台设置的密钥key
     */
    @Override
    public String createWxPaymentSign(WxPayment wxPayment, String apiKey) throws Exception {
        // 创建签名
        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("total_fee", wxPayment.getTotalFee() + "");
        packageParams.put("appid", wxPayment.getAppId());
        packageParams.put("mch_id", wxPayment.getMchId());
        packageParams.put("nonce_str", wxPayment.getNonceStr());
        packageParams.put("sign_type", wxPayment.getSignType());
        packageParams.put("body", wxPayment.getBody());
        packageParams.put("out_trade_no", wxPayment.getOutTradeNo());
        packageParams.put("fee_type", wxPayment.getFeeType());
        packageParams.put("spbill_create_ip", wxPayment.getSpbillCreateIp());
        packageParams.put("notify_url", wxPayment.getNotifyUrl());
        packageParams.put("trade_type", wxPayment.getTradeType());
        packageParams.put("openid", wxPayment.getOpenid());
        packageParams.put("attach", wxPayment.getAttach());
        return PayUtils.createSign(packageParams, apiKey);
    }

    /**
     * 微信支付调用统一下单接口
     *
     * @author LiQun
     * @date 2019/2/26 14:37
     * @param wxPayment 微信付款对象
     * @return map对象,包含付款请求相应信息
     * @throws Exception 异常信息
     */
    @Override
    public Map<String, Object> unifiedOrder(WxPayment wxPayment) throws Exception {
        Map<String, Object> result = new HashMap<>(16);
        XStream xstream = new XStream();
        xstream.processAnnotations(wxPayment.getClass());
        //生成请求参数XML
        String xml = xstream.toXML(wxPayment);
        xml = xml.replace("__", "_");
        //请求url
        String url = PayUtils.PAY_UNIFIED_ORDER_URL;
        // 记录发送请求日志
        wxLogService.log(WxLogModule.wxLogPay, "调用微信支付调用统一下单接口-发送请求", JsonUtil.toString(xml), wxPayment.getAppId(), null);
        //发送请求
        result = PayUtils.getPayNo(url, xml);
        wxLogService.log(WxLogModule.wxLogPay, "调用微信支付调用统一下单接口-相应结果", JsonUtil.toString(result), wxPayment.getAppId(), null);
        return result;
    }


    @Override
    public CoPayToWalletPayment getCoPayToWalletPayment(
            String appId,
            String mchId,
            String openId,
            Integer amount,
            String partnerTradeNo,
            String apiKey,
            PayUtils.ForceCheck forceCheck,
            String reUserName,
            String desc
    ) throws Exception {
        if (StringUtils.isBlank(appId)) {
            throw new ServiceException("PAYMENT-007");
        }
        if (StringUtils.isBlank(mchId)) {
            throw new ServiceException("PAYMENT-008");
        }
        if (StringUtils.isBlank(openId)) {
            throw new ServiceException("PAYMENT-006");
        }
        if (Objects.isNull(amount) || amount <= 0) {
            throw new ServiceException("PAYMENT-009");
        }
        if (StringUtils.isBlank(partnerTradeNo)) {
            throw new ServiceException("PAYMENT-010");
        }
        if (StringUtils.isBlank(apiKey)) {
            throw new ServiceException("PAYMENT-005");
        }
        if (PayUtils.ForceCheck.FORCE_CHECK.equals(forceCheck)) {
            if (StringUtils.isBlank(reUserName)) {
                throw new ServiceException("PAYMENT-011");
            }
        }
        if (StringUtils.isBlank(desc)) {
            throw new ServiceException("PAYMENT-012");
        }

        //8位日期
        String currTime = PayUtils.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandomNo = String.valueOf(PayUtils.buildRandom(6));
        partnerTradeNo = strTime + strRandomNo;

        CoPayToWalletPayment coPayToWalletPayment = new CoPayToWalletPayment();
        // 商户账号appid
        coPayToWalletPayment.setMchAppid(appId);
        // 商户号
        coPayToWalletPayment.setMchid(mchId);
        // 付款金额
        coPayToWalletPayment.setAmount(amount);
        // 随机字符串
        coPayToWalletPayment.setNonceStr(PayUtils.createNonceStr());
        // 付款用户的openId
        coPayToWalletPayment.setOpenid(openId);
        // 商户订单号
        coPayToWalletPayment.setPartnerTradeNo(partnerTradeNo);
        // 强校验姓名
        coPayToWalletPayment.setCheckName(forceCheck.toString());
        // 如果强校验姓名,则需要填写真实姓名
        coPayToWalletPayment.setReUserName("");
        // 终端ip
        InetAddress addr = InetAddress.getLocalHost();
        coPayToWalletPayment.setSpbillCreateIp(addr.getHostAddress());
        coPayToWalletPayment.setDesc(desc);
        // 签名
        coPayToWalletPayment.setSign(createCoPayToWalletPaymentSign(coPayToWalletPayment, apiKey));

        wxLogService.log(WxLogModule.wxLogPay, "创建微信支付对象", JsonUtil.toString(coPayToWalletPayment), appId, null);

        return coPayToWalletPayment;
    }

    /**
     * 创建企业付款到零钱签名
     *
     * @author LiQun
     * @date 2019/2/27 09:15
     * @param coPayToWalletPayment 企业付款到零钱对象
     * @param apiKey 为商户平台设置的密钥key
     */
    @Override
    public String createCoPayToWalletPaymentSign(CoPayToWalletPayment coPayToWalletPayment, String apiKey) throws Exception {
        SortedMap<String, String> packageParams = new TreeMap<>();
        packageParams.put("mch_appid", coPayToWalletPayment.getMchAppid());
        packageParams.put("mchid", coPayToWalletPayment.getMchid());
        packageParams.put("nonce_str", coPayToWalletPayment.getNonceStr());
        packageParams.put("partner_trade_no", coPayToWalletPayment.getPartnerTradeNo());
        packageParams.put("openid", coPayToWalletPayment.getOpenid());
        packageParams.put("check_name", coPayToWalletPayment.getCheckName());
        packageParams.put("amount", String.valueOf(coPayToWalletPayment.getAmount()));
        packageParams.put("desc", coPayToWalletPayment.getDesc());
        packageParams.put("spbill_create_ip", coPayToWalletPayment.getSpbillCreateIp());
        packageParams.put("appid", coPayToWalletPayment.getAppid());
        return PayUtils.createSign(packageParams, apiKey);
    }

    /**
     * 发送企业付款到零钱请求
     *
     * @author LiQun
     * @date 2019/2/27 10:53
     */
    @Override
    public Map<String,Object> sendCoPayToWalletPaymentRequest(CoPayToWalletPayment coPayToWalletPayment) throws Exception {
        Map<String, Object> result = new HashMap<>(16);
        XStream xstream = new XStream();
        xstream.processAnnotations(coPayToWalletPayment.getClass());
        //生成请求参数XML
        String xml = xstream.toXML(coPayToWalletPayment);
        xml = xml.replace("__", "_");
        //请求url
        String url = PayUtils.MP_MCHPAY_URL;
        wxLogService.log(WxLogModule.wxLogPay, "调用微信支付调用统一下单接口-发送请求", JsonUtil.toString(xml), coPayToWalletPayment.getMchAppid(), null);
        //发送请求
        result = PayUtils.post(xml, url, coPayToWalletPayment.getMchid());
        wxLogService.log(WxLogModule.wxLogPay, "调用微信支付调用统一下单接口-相应结果", JsonUtil.toString(result), coPayToWalletPayment.getMchAppid(), null);
        return result;
    }
}
